/*** In The Name of Allah ***/
package com.codeanalysissys.backend.python;

import com.codeanalysissys.backend.python.parser.PythonLexer;
import com.codeanalysissys.backend.python.parser.PythonParser;
import com.codeanalysissys.backend.python.parser.PythonParserBaseVisitor;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
 * A utility class for building PythonClass structures from a given Python source file.
 *
 * @author WenLiang Peng
 */
public class PythonClassExtractor {

	public static List<PythonClass> extractInfo(String pythonFile) throws IOException {
		return extractInfo(new File(pythonFile));
	}

	public static List<PythonClass> extractInfo(File pythonFile) throws IOException {
		return extractInfo(pythonFile.getAbsolutePath(), new FileInputStream(pythonFile));
	}

    public static List<PythonClass> extractInfo(String pythonFilePath, InputStream inStream) throws IOException {
        ANTLRInputStream input = new ANTLRInputStream(inStream);
        PythonLexer lexer = new PythonLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        PythonParser parser = new PythonParser(tokens);
        ParseTree tree = parser.root();
        return extractInfo(pythonFilePath, tree);
    }

    public static List<PythonClass> extractInfo(ParseTree tree) {
        PythonClassVisitor visitor = new PythonClassVisitor();
        return visitor.build(tree);
    }

    public static List<PythonClass> extractInfo(String pythonFilePath, ParseTree tree) {
        PythonClassVisitor visitor = new PythonClassVisitor(pythonFilePath);
        return visitor.build(tree);
    }


    private static int countSlashes(String str) {
        int slashCount = 0;
        for (char chr : str.toCharArray())
			if (chr == '/')
				++slashCount;
		return slashCount;
	}

    private static class PythonClassVisitor extends PythonParserBaseVisitor<String> {

        private String filePath;
        private int methodNumNoClass = 0;
        private List<String> importsList;
        private List<PythonClass> pythonClasses;
        private Deque<PythonClass> activeClasses;

        public PythonClassVisitor(String path) {
            filePath = path;
        }

        public PythonClassVisitor() {
            filePath = null;
        }

        public List<PythonClass> build(ParseTree tree) {
            // python do not need the package name、static、abstract
            pythonClasses = new ArrayList<>();
            importsList = new ArrayList<>();
            activeClasses = new ArrayDeque<>();
			visit(tree);
			pythonClasses.add(!activeClasses.isEmpty() ? activeClasses.pop() : null);
            return pythonClasses;
        }

        @Override
        public String visitImport_stmt(PythonParser.Import_stmtContext ctx) {
            // IMPORT dotted_as_names
			String qualifiedName = "import ";
			for (int i = 0; i < ctx.dotted_as_names().COMMA().size(); i++){
				if (i == 0){
					qualifiedName = qualifiedName + ctx.dotted_as_names().dotted_as_name(i).getText();
				}else {
					qualifiedName = qualifiedName + ", " + ctx.dotted_as_names().dotted_as_name(i).getText();
				}
			}
			importsList.add(qualifiedName);
            return null;
        }

		@Override
		public String visitFrom_stmt(PythonParser.From_stmtContext ctx) {
			//      IMPORT (STAR | OPEN_PAREN import_as_names CLOSE_PAREN | import_as_names)
			//import_as_names
			//    : import_as_name (COMMA import_as_name)* COMMA?
			//    ;import_as_name
			//    : name (AS name)?
			//    ;
			String qualifiedName = "from ";
			for(int i = 0; i < ctx.DOT().size(); i++){
				qualifiedName = qualifiedName + ctx.DOT(i).getText();
			}
			qualifiedName = (ctx.dotted_name() != null) ? qualifiedName + ctx.dotted_name().getText() : qualifiedName;
			qualifiedName = qualifiedName + " import ";
			if (ctx.STAR() != null){
				qualifiedName = qualifiedName + ctx.STAR().getText();
			}else{
				if (ctx.import_as_names() != null){
					assert ctx.import_as_names() instanceof PythonParser.Import_as_namesContext;
					qualifiedName = (ctx.OPEN_PAREN() != null) ? qualifiedName + '(' : qualifiedName;
					int com_length = ctx.import_as_names().COMMA().size();
					for (int i = 0; i < ctx.import_as_names().import_as_name().size(); i++){
						PythonParser.Import_as_nameContext temAsName = ctx.import_as_names().import_as_name(i);
						qualifiedName = (temAsName.AS() != null) ? qualifiedName + temAsName.name(0).getText() + " as " + temAsName.name(1).getText() : qualifiedName;
						if (com_length-- != 0) qualifiedName = qualifiedName + ',';
					}
				}else{
					//这种情况不存在
				}
			}
			importsList.add(qualifiedName);
			return null;
		}

		@Override
		public String visitClassdef(PythonParser.ClassdefContext ctx) {
			//   : CLASS name (OPEN_PAREN arglist? CLOSE_PAREN)? COLON suite
			List<String> exTends = new ArrayList<>();
			if (ctx.arglist() != null){
				for (PythonParser.ArgumentContext tem : ctx.arglist().argument()){
					exTends.add(tem.getText());
				}
			}
			PythonClass pls = new PythonClass(ctx.name().getText(),filePath,importsList.toArray(new String[importsList.size()]),exTends.toArray(new String[exTends.size()]));
			activeClasses.push(pls);
			visit(ctx.suite());
			// if no class then class not exist
			pythonClasses.add(activeClasses.pop());        // python都是动态执行的
			return null;
		}

		@Override
		public String visitFuncdef(PythonParser.FuncdefContext ctx) {
			//  : ASYNC? DEF name OPEN_PAREN typedargslist? CLOSE_PAREN (ARROW test)? COLON suite
			//    ;
			String type = null;
			if (ctx.test() != null){
				type = ctx.test().getText();
			}
			String name = ctx.name().getText();
			String[] args = null;
			boolean async = (ctx.ASYNC() != null) ? true : false;
			List<String> argsList = new ArrayList<>();
			if (ctx.typedargslist() != null) {
				if(ctx.typedargslist().def_parameters() != null){
					for(PythonParser.Def_parametersContext tem : ctx.typedargslist().def_parameters()){
						argsList.add(tem.getText());
					}
				}
				if(ctx.typedargslist().args() != null) argsList.add(ctx.typedargslist().args().getText());
				if(ctx.typedargslist().kwargs() != null) argsList.add(ctx.typedargslist().kwargs().getText());
			}
			if (argsList.size() > 0){
				args = argsList.toArray(new String[argsList.size()]);
			}
			int line = ctx.getStart().getLine();

			// deal with the no class situation
			if (activeClasses.peek() == null || activeClasses.isEmpty()) {
				activeClasses.clear();
				PythonClass temClass = new PythonClass("temFile","temPath", null, null);
				activeClasses.push(temClass);
			}
			activeClasses.peek().addMethod(new PythonMethod(name, args, type, line, async));
			return null;
		}
	}
}
